package cn.com.game.link.main;

import cn.com.game.link.core.Game;
import cn.com.game.link.core.GamePanel;
import cn.com.game.link.entity.LinkItem;
import cn.com.game.link.system.RenderSystem;
import cn.com.game.link.utils.ImageResource;
import cn.com.game.link.utils.TextUtil;

import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.RoundRectangle2D;
import java.util.LinkedList;
import java.util.Objects;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Random;
import java.util.Stack;


public class LinkGame extends Game implements MouseListener {
	
	public static final int ROW = 10;
	public static final int COL = 14;
	public static final int PADDING = 2;
	public static final int BORDER_WIDTH = PADDING * 10;
	
	public static final int WIDTH = LinkItem.LINK_ITEM_WIDTH * COL + (COL - 1) * PADDING + BORDER_WIDTH * 2;
	public static final int HEIGHT = LinkItem.LINK_ITEM_WIDTH * ROW + (ROW - 1) * PADDING + BORDER_WIDTH * 2;
	private RenderSystem renderSystem;
	private Random rand;
	private LinkItem[][] linkItems ;

	
	public LinkGame() {
		super(LinkGame::buildGamePanel);
		addMouseListener();
	}
	
	private void addMouseListener() {
		this.panel.addMouseListener(this);
	}

	@Override
	protected void initialzer() {
		linkItems = new LinkItem[ROW][COL];
		renderSystem = new RenderSystem();
		rand = new Random();
	
		for (int i = 0; i < COL * ROW / 2; i++) {
			int type = rand.nextInt(ImageResource.ICONS.length);
			renderSystem.add(random(type));
			renderSystem.add(random(type));
		}
		
	}
	
	private void generate() {
		for (int y = 0; y < ROW; y++) {
			for (int x = 0; x < COL; x++) {
				linkItems[y][x].setReady(false);
			}
		}
		for (int i = 0; i < COL * ROW / 2; i++) {
			int type = rand.nextInt(10);
			this.random(type);
			this.random(type);
		}
	}
	
	private LinkItem random(int type) {
		Point location = success ? reRandomLocation() : randomLocation();
		if (null == linkItems[location.y][location.x]) {
			linkItems[location.y][location.x] = new 
				LinkItem(BORDER_WIDTH 
						+ location.x * LinkItem.LINK_ITEM_WIDTH 
						+ location.x * PADDING,
						BORDER_WIDTH
						+ location.y * LinkItem.LINK_ITEM_HEIGHT
						+ location.y * PADDING, type);
			linkItems[location.y][location.x].setLocation(location);
			linkItems[location.y][location.x].setIconRender(this::renderItemIcon);
		} else {
			linkItems[location.y][location.x].setType(type);
			linkItems[location.y][location.x].setSelected(false);
			linkItems[location.y][location.x].setCleaned(false);
		}
		System.out.println("（"+location.x+","+location.y+"）：" + linkItems[location.y][location.x]);
		return linkItems[location.y][location.x];
	}
	
	
	private Point randomLocation() {
		LinkItem item = null;
		int x =0, y = 0;
		do {
			x = rand.nextInt(COL);
			y = rand.nextInt(ROW);
			item = linkItems[y][x];
		} while (item != null);
		return new Point(x, y);
	}
	private Point reRandomLocation() {
		LinkItem item = null;
		int x =0, y = 0;
		do {
			x = rand.nextInt(COL);
			y = rand.nextInt(ROW);
			item = linkItems[y][x];
		} while (item.isReady());
		if (Objects.nonNull(item)) {
			item.setReady(true);
		}
		return new Point(x, y);
	}
	
	private boolean success = false;
	
	@Override
	public void update(double deltaU) {
		boolean isCleaned = true;
		for (int y = 0; y < linkItems.length; y++) {
			for (int x = 0; x < linkItems[y].length; x++) {
				linkItems[y][x].update(deltaU);
				if (!linkItems[y][x].isCleaned()) {
					isCleaned = false;
				}
			}
		}
		if (isCleaned) {
			success = true;
		}
	}
	private Rectangle successTextHitbox = new Rectangle(0, 0, WIDTH, HEIGHT / 3 * 2);
	private Rectangle btnHitBox =  new Rectangle(
			(WIDTH - 100) / 2,
			HEIGHT / 2 ,
			100,
			50
			);
	@Override
	public void render(Graphics g) {
		g.setColor(Color.BLACK);
		g.fillRect(0, 0, WIDTH, HEIGHT);
		// 抗锯齿
		((Graphics2D)g).setRenderingHint(
				java.awt.RenderingHints.KEY_ANTIALIASING,
				java.awt.RenderingHints.VALUE_ANTIALIAS_ON);
		
		if (success) {
			g.setColor(Color.WHITE);
			g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 30));
		
			TextUtil.center(successTextHitbox, "恭喜您！！！")
			.accept(g);
			g.fillRect(btnHitBox.x, btnHitBox.y, btnHitBox.width, btnHitBox.height);
			g.setColor(Color.BLACK);
			g.setFont(new Font(Font.SANS_SERIF, Font.PLAIN, 12));
			TextUtil.center(btnHitBox, "再来一局")
			.accept(g);
			
			return;
		}
		
		
		renderSystem.render(g);
		if (drawConnectLine) {
			g.setColor(drawConnectColor);
			for (int i = 0; i < drawConnectLinePath.size() - 1; i++) {
			 LinkItem start =	drawConnectLinePath.get(i);
			 LinkItem end =	drawConnectLinePath.get(i+ 1);
				g.drawLine(
						start.getHitbox().x + LinkItem.LINK_ITEM_WIDTH / 2,
						start.getHitbox().y + LinkItem.LINK_ITEM_HEIGHT / 2,
						end.getHitbox().x + LinkItem.LINK_ITEM_WIDTH / 2,
						end.getHitbox().y + LinkItem.LINK_ITEM_HEIGHT / 2);
			}
			drawConnectLineTime--;
			if (drawConnectLineTime < 0) {
				drawConnectLineTime = 10;
				drawConnectLine = false;
			}
		}
		
	}



	private static GamePanel buildGamePanel(Game game) {
		return new GamePanel(game, WIDTH, HEIGHT);
	}
	
	private LinkItem prevLinkItem;
	
	@Override
	public void mouseClicked(MouseEvent e) {
		
	}

	private void checkLinkItem(LinkItem start, LinkItem end) {
		if(start.getType() != end.getType()) {
			prevLinkItem = end;
			return;
		}
		int xOffset = start.getX() - end.getX(); // < 0 向右遍历 > 0 向左遍历 
		int yOffset = start.getY() - end.getY(); // < 0 向下遍历 > 0 向上遍历
		if ((xOffset == 0 && Math.abs(yOffset) == 1) || 
				(Math.abs(xOffset) == 1 && yOffset == 0)) { // 直连
		  drawConnectLine = true;
		  LinkedList<LinkItem> newPath = new LinkedList<LinkItem>();
		  newPath.add(start);
		  newPath.add(end);
          drawConnectLinePath = newPath;
			start.setCleaned(true);
			end.setCleaned(true);
		} else if (canConnect(start, end, xOffset, yOffset)) {
			start.setCleaned(true);
			end.setCleaned(true);
		}
		prevLinkItem = null;
    }
	

	private static final int[][] DIRECTIONS = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}}; // 四个方向：右、左、下、上
    
    private LinkedList<LinkItem> drawConnectLinePath;
    private boolean drawConnectLine = false;
    private int drawConnectLineTime = 10;
    private Color drawConnectColor = Color.GREEN;
	// 深度优先搜索
    public boolean dfs(LinkItem start, LinkItem target) {
        boolean[][] visited = new boolean[ROW][COL];
        Stack<Node> stack = new Stack<>();
        LinkedList<LinkItem> initialPath = new LinkedList<>();
        initialPath.add(start);
        stack.push(new Node(start, 0, initialPath, 0));
        visited[start.getY()][start.getX()] = true;
        while (!stack.isEmpty()) {
        	Node current = stack.pop();
        	int dirIndex = 0;
            for (int[] dir : DIRECTIONS) {
            	if (current.item.getY() + dir[1] < 0 || 
            			current.item.getY() + dir[1] >= ROW||
            					current.item.getX() + dir[0] < 0 ||
            					current.item.getX() + dir[0] >= COL 
            			) {
            		continue;
            	}
            	LinkItem next = linkItems[current.item.getY() + dir[1]][current.item.getX() + dir[0]];
                if (isValid(next) && !visited[next.getY()][next.getX()]) {
                	 LinkedList<LinkItem> newPath = new LinkedList<>(current.path);
                     newPath.add(next);
                     int turnCount = calculateTurns(current, next);
                	if (next.getX() == target.getX() && next.getY() == target.getY() ) {
                		 printPath(newPath);
                         System.out.println("Turn count: " + turnCount);
                        if (turnCount <= 2) {
                    	  drawConnectLine = true;
                          drawConnectLinePath = newPath;
                          return true;
                        }
                    }
                    if (next.isCleaned()) {
                        visited[next.getY()][next.getX()] = true;
                        stack.push(new Node(next, turnCount, newPath, dirIndex));
                    }
                }
                dirIndex ++;
            }
        }
        return false;
    }

    
    public void renderItemIcon(Graphics g, LinkItem item) {
    	g.setColor(Color.WHITE);
		Graphics2D g2d = (Graphics2D) g;
		RoundRectangle2D.Double rect = new RoundRectangle2D.Double(
				item.getHitbox().x,
				item.getHitbox().y,
				item.getHitbox().width,
				item.getHitbox().height, 4, 4);
		g2d.fill(rect);



		g.drawImage(
				ImageResource.ICONS[item.getType()],
				item.getHitbox().x, 
				item.getHitbox().y, 
				item.getHitbox().width, 
				item.getHitbox().height, this.panel);
    }
    

    public Node bfs(LinkItem start, LinkItem target) {
        boolean[][][] visited = new boolean[ROW][COL][4];
        Queue<Node> queue = new PriorityQueue<>();
        LinkedList<LinkItem> initialPath = new LinkedList<>();
        initialPath.add(start);
        for (int dir = 0; dir < 4; dir++) {
            queue.add(new Node(start, 0, initialPath, dir));
		}
        
        while (!queue.isEmpty()) {
            Node current = queue.poll();
            int dirIndex = 0;
            if (current.item.getX() == target.getX()
            		&& current.item.getY() == target.getY()) {
            	 return current;	
            }
            for (int[] dir : DIRECTIONS) {
            	if (current.item.getY() + dir[1] < 0 || 
            			current.item.getY() + dir[1] >= ROW||
            					current.item.getX() + dir[0] < 0 ||
            					current.item.getX() + dir[0] >= COL 
            			) {
            		continue;
            	}
            	LinkItem next = linkItems[current.item.getY() + dir[1]][current.item.getX() + dir[0]];
                if (!visited[next.getY()][next.getX()][dirIndex]) {
	               LinkedList<LinkItem> newPath = new LinkedList<>(current.path);
	               newPath.add(next);
//	               System.out.println("current.dir: " + current.dir + "dirIndex:" + dirIndex + " => " + (current.dir == dirIndex));
                   int turnCount = calculateTurns(current, next);
                   if (next.isCleaned() || 
                		   (next.getX() == target.getX() && next.getY() == target.getY())
                		   ) {
                       visited[next.getY()][next.getX()][dirIndex] = true;
                       queue.add(new Node(next, turnCount, newPath, dirIndex));
                   }
                }
                dirIndex++;
            }
        }
        return null;
    }
    
    
    // 检查坐标是否有效
    private boolean isValid(LinkItem coord) {
        int x = coord.getX();
        int y = coord.getY();
        return x >= 0 && x < COL && y >= 0 && y < ROW;
    }
    

    // 计算转弯次数
    private int calculateTurns(Node current, LinkItem next) {
    	if (current.path.size() < 2) return 0;
    	LinkItem last = current.path.getLast();
    	LinkItem secondLast = current.path.get(current.path.size() - 2);
        int dx1 = last.getX() - secondLast.getX();
        int dy1 = last.getY()- secondLast.getY();
        int dx2 = next.getX() - last.getX();
        int dy2 = next.getY() - last.getY();
        if ((dx1 == 0 && dx2 == 0) || (dy1 == 0 && dy2 == 0)) {
            return current.turnCount;
        } else {
            return current.turnCount + 1;
        }
    }

    // 打印路径
    private void printPath(LinkedList<LinkItem> path) {
        for (LinkItem coord : path) {
            System.out.println("(" + coord.getX() + ", " +coord.getY() + ")");
        }
    }
    
	private boolean canConnect(LinkItem start, LinkItem end,
			int xOffset, int yOffset) {
	 Node node1 = bfs(start, end);
	 if (null != node1) {
		 Node tagetNode = node1;
       	 drawConnectLine = true;
         drawConnectLinePath = tagetNode.path;
         System.out.println("转弯次数：" + tagetNode.turnCount);
         if (tagetNode.turnCount < 3) {
        	 drawConnectColor = Color.GREEN;
             return true;
         } else {
        	 drawConnectColor = Color.RED;
             return false;
         }
	 }
		return false;
	}

	@Override
	public void mousePressed(MouseEvent e) {}
	@Override
	public void mouseReleased(MouseEvent e) {
		if (success && btnHitBox.contains(e.getX(), e.getY())) {
			generate();
			success = false;
			drawConnectLine = false;
			return;
		}
		for (int y = 0; y < linkItems.length; y++) {
			for (int x = 0; x < linkItems[y].length; x++) {
				if(linkItems[y][x].click(e.getX(), e.getY())) {
					if (!linkItems[y][x].isSelected()) continue;
					if (null == prevLinkItem) {
						prevLinkItem = linkItems[y][x];
					}
					if (prevLinkItem != linkItems[y][x]) {
						checkLinkItem(prevLinkItem,  linkItems[y][x]);
					}
				} else {
					linkItems[y][x].setSelected(false);
				}
			}
		}
	}
	@Override
	public void mouseEntered(MouseEvent e) {}
	@Override
	public void mouseExited(MouseEvent e) {}

	
	class Node implements Comparable<Node>{
	    LinkItem item;
	    int turnCount;
	    LinkedList<LinkItem> path;
	    int dir;

	    public Node(LinkItem item, int turnCount, LinkedList<LinkItem> path,int dir) {
	        this.item = item;
	        this.turnCount = turnCount;
	        this.path = path;
	        this.dir = dir;
	    }
	    
	    @Override
	    public int compareTo(Node other) {
	        return Integer.compare(this.turnCount, other.turnCount);
	    }
	}
	

}
